package org.systemsbiology.bigquery.gae.servlets;

import com.google.api.services.bigquery.model.*;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.systemsbiology.bigquery.gae.dao.QueriesPayload;
import org.systemsbiology.bigquery.gae.utils.BQUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.commons.lang.StringUtils.*;
import static org.systemsbiology.bigquery.gae.dao.Queries.getQuery;
import static org.systemsbiology.bigquery.gae.dao.Queries.saveQuery;
import static org.systemsbiology.bigquery.gae.utils.JobUtils.*;

/**
 * @author hrovira
 */
public class QueryResultsServlet extends HttpServlet {
    private static final Logger log = Logger.getLogger(QueryResultsServlet.class.getName());

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            BQUtils bqUtils = new BQUtils();

            String uri = request.getRequestURI();
            if (equalsIgnoreCase(uri, "/queries")) {
                List<JobListJobs> jobs = bqUtils.lookupJobs();
                if (jobs == null) {
                    response.setStatus(SC_NOT_FOUND);
                    return;
                }

                Iterator<JobListJobs> itr = jobs.iterator();
                JsonArray items = new JsonArray();
                while (itr.hasNext()) {
                    JobListJobs jlj = itr.next();
                    String jobId = jlj.getJobReference().getJobId();

                    JsonObject jobjson = new JsonObject();
                    jobjson.addProperty("id", jobId);
                    jobjson.addProperty("uri", "/queries/" + jobId);
                    jobjson.addProperty("status", jlj.getState());
                    if (jlj.getStatistics() != null) {
                        jobjson.addProperty("startTimeFromEpoch", jlj.getStatistics().getStartTime());
                    }

                    QueriesPayload payload = getQuery(jobId);
                    if (payload != null) {
                        jobjson.addProperty("tq", payload.getTq());
                        jobjson.addProperty("tableId", payload.getTableId());
                        jobjson.addProperty("payload", payload.getPayload());
                    }

                    if (jlj.getErrorResult() != null && jlj.getErrorResult().getMessage() != null) {
                        jobjson.addProperty("errorMsg", jlj.getErrorResult().getMessage());
                    }
                    items.add(jobjson);
                }

                JsonObject json = new JsonObject();
                json.add("items", items);
                json.addProperty("numberOfItems", items.size());
                response.setContentType("application/json");
                response.getWriter().write(json.toString());

                return;
            }

            String jobId = substringAfterLast(uri, "/queries/");
            Job job = bqUtils.lookupJob(jobId);
            if (job == null) {
                response.setStatus(SC_NOT_FOUND);
                return;
            }

            log.info("job=" + job.toPrettyString());

            if (jobIsDone(job) && isSuccessful(job)) {
                List<Bigqueryfield> fields = bqUtils.getSchemaFieldNames(job);

                JsonObject json = new JsonObject();
                json.add("data", getResults(fields, bqUtils.getTableData(job)));
                json.add("dataSchema", getDataSchema(fields));
                response.setContentType("application/json");
                response.getWriter().write(json.toString());
                return;
            }

            String msg = getErrorMessage(job);
            if (isEmpty(msg)) {
                JsonObject json = new JsonObject();
                json.addProperty("uri", "/queries/" + jobId);
                json.addProperty("message", "Job is not yet ready");

                response.setContentType("application/json");
                response.getWriter().write(json.toString());
                return;
            }

            response.setStatus(SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write(msg);
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            String tq = request.getParameter("tq");
            if (isEmpty(tq)) {
                response.setStatus(SC_BAD_REQUEST);
                return;
            }

            String tableId = request.getParameter("tableId");
            String payload = request.getParameter("payload");

            BQUtils bqUtils = new BQUtils();
            Job job = bqUtils.createJob(tq);

            String jobId = job.getJobReference().getJobId();
            saveQuery(jobId, new QueriesPayload(tq, tableId, payload));

            JsonObject json = new JsonObject();
            json.addProperty("jobId", jobId);
            json.addProperty("uri", "/queries/" + jobId);
            json.addProperty("query", tq);
            response.setContentType("application/json");
            response.getWriter().write(json.toString());
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }

    private JsonArray getResults(List<Bigqueryfield> fields, List<Row> rows) {
        log.info("fields=" + fields.size());
        log.info("rows=" + rows.size());
        JsonArray results = new JsonArray();

        for (Row row : rows) {
            JsonObject result = new JsonObject();

            Iterator<Bigqueryfield> fItr = fields.iterator();
            Iterator<RowF> rItr = row.getF().iterator();

            while (fItr.hasNext() && rItr.hasNext()) {
                String name = fItr.next().getName();
                Object value = rItr.next().getV();
                if (value != null) {
                    result.addProperty(name, value.toString());
                }
            }

            results.add(result);
        }

        log.info("results=" + results.size());
        return results;
    }

    private JsonArray getDataSchema(List<Bigqueryfield> fields) throws Exception {
        JsonArray columns = new JsonArray();
        for (Bigqueryfield field : fields) {
            JsonObject column = new JsonObject();
            column.addProperty("name", field.getName());
            column.addProperty("type", field.getType());
            column.addProperty("mode", field.getMode());
            columns.add(column);
        }
        return columns;
    }

}
